home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 22 code / Paper Juggling / JuggleRoutines.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-13  |  12.9 KB  |  494 lines  |  [TEXT/MMCC]

  1. /*--------------------------------------------------------------------
  2.     JuggleRoutines.c
  3.     
  4.     Routines that manipulate the throws data, and high level routines
  5.     
  6. ----------------------------------------------------------------------*/
  7.  
  8. #include "PaperJuggling.h"
  9.  
  10. //----------------------------------------------------
  11. // Init and CleanUp
  12.  
  13. OSErr InitJuggle(WindowPtr wind, short numJugglers, short numCounts)
  14. {
  15.     HandHandle        throwsHandle;
  16.     long            throwsSize;
  17.     short            count;
  18.     JuggleHandle    aJuggle;
  19.     
  20.     aJuggle = GetWindowJuggle(wind);
  21.     if(aJuggle == nil)
  22.         return -1;
  23.         
  24.     // Try to allocate the throws
  25.     throwsSize = (numCounts * numJugglers * sizeof(Hand));
  26.     throwsHandle = (HandHandle) NewHandleClear(throwsSize);
  27.     if(throwsHandle == nil)
  28.         return -108;
  29.     
  30.     // Keep the throws
  31.     (*aJuggle)->theThrows = throwsHandle;
  32.     
  33.     // Set up initial screen map
  34.     for(count = 0; count < numJugglers; count++)
  35.     {
  36.         (*aJuggle)->jugglerMap[count] = count + 1;
  37.     }
  38.  
  39.     // State variables
  40.     (*aJuggle)->gridPt.x = kDefaultGridSpace;
  41.     (*aJuggle)->gridPt.y = kDefaultGridSpace;
  42.     (*aJuggle)->numJugglers = numJugglers;
  43.     (*aJuggle)->numCounts = numCounts;
  44.     
  45.     // Set the contentSize of the doc
  46.     ResetJuggleContentSize(aJuggle);
  47.     
  48.     // Set up the graphics
  49.     if(SetUpJuggleGraphics(wind, aJuggle) == nil)
  50.     {
  51.         DisposeHandle((Handle)throwsHandle);
  52.         (*aJuggle)->theThrows = nil;
  53.     }
  54.     return noErr;
  55. }
  56.  
  57. void CleanUpJuggle(WindowPtr window, Boolean graphicsToo)
  58. {
  59.     JuggleHandle    aJuggle = GetWindowJuggle(window);
  60.     
  61.     if(graphicsToo)
  62.         CleanUpJuggleShapes(window);
  63.     DisposeHandle((Handle)(*aJuggle)->theThrows);    // Dispose of throws data
  64. }
  65.  
  66. //----------------------------------------------------
  67. // Accessor routine for the juggle
  68.  
  69.  
  70. JuggleHandle GetWindowJuggle (WindowPtr wind)
  71. {
  72.     JuggleHandle    jug = nil;
  73.  
  74.     if ( wind != nil )
  75.         jug = (JuggleHandle) GetWRefCon(wind);
  76.     
  77.     return jug;
  78. }
  79.  
  80.  
  81. //----------------------------------------------------
  82. // throw manipulation routines
  83.  
  84. // Resize the throws in the juggle, copying over as many throws as possible if indicated.
  85. // Returns true if there are no errors
  86. Boolean ResizeThrows(JuggleHandle aJuggle, short newJugglers, short newCounts,
  87.                             Boolean copyThrows)
  88. {
  89.     short         oldJugglers, oldCounts;
  90.     long        throwsSize;
  91.     HandHandle    newThrows, oldThrows;
  92.     
  93.     oldJugglers = (*aJuggle)->numJugglers;
  94.     oldCounts = (*aJuggle)->numCounts;
  95.     oldThrows = (*aJuggle)->theThrows;
  96.     
  97.     // Range checking
  98.     if(newJugglers == oldJugglers && newCounts == oldCounts)
  99.         return true; // no change, do nothing
  100.     if(newJugglers > kMaxJugglers || newJugglers < 1 || newCounts < 2)
  101.         return false;
  102.     
  103.     // allocate the new throws
  104.     throwsSize = (newCounts * newJugglers * sizeof(Hand));
  105.     newThrows = (HandHandle) NewHandleClear(throwsSize);
  106.     if( newThrows == nil )
  107.         return false;
  108.  
  109.     // if copyThrows is true, copy over what throws we can
  110.     if(copyThrows == true)
  111.     {
  112.         CopyThrows(oldThrows, oldJugglers, oldCounts,
  113.                     newThrows, newJugglers, newCounts);
  114.     }
  115.     
  116.     // Dispose old throws
  117.     DisposeHandle((Handle)oldThrows);
  118.     
  119.     // update the juggle
  120.     (*aJuggle)->numJugglers = newJugglers;
  121.     (*aJuggle)->numCounts = newCounts;
  122.     (*aJuggle)->theThrows = newThrows;
  123.     
  124.     return true;
  125. }
  126.  
  127. // Fast, no range checking, most routines call GetHand (below) instead
  128. HandPtr GetHandPtr(HandHandle throws, short numCounts, short juggler, short count)
  129. {
  130.     HandPtr result = *throws;
  131.     
  132.     // increment the right amount to get to the hand we want
  133.     return (result + ((juggler - 1) * numCounts) + (count - 1) );
  134. }
  135.  
  136. // Given the HandLoc, return a pointer to the specific hand
  137. HandPtr GetHand(JuggleHandle aJuggle, HandLoc theLoc, Boolean wrapIt)
  138. {
  139.     // Make sure we're in range, wrapping if indicated
  140.     if(theLoc.time > (*aJuggle)->numCounts)
  141.     {
  142.         if(wrapIt)
  143.         {
  144.             theLoc.time %= (*aJuggle)->numCounts;
  145.             // could be zero, if the input was a multiple of numCounts.
  146.             // if so, make it numCounts instead
  147.             if(theLoc.time == 0)
  148.                 theLoc.time = (*aJuggle)->numCounts;
  149.         }
  150.         else
  151.             return nil;
  152.     }
  153.     else if(theLoc.time < 1)
  154.         return nil;
  155.     if(theLoc.juggler > (*aJuggle)->numJugglers || theLoc.juggler < 1)
  156.         return nil;
  157.     
  158.     return GetHandPtr((*aJuggle)->theThrows, (*aJuggle)->numCounts, theLoc.juggler, theLoc.time);
  159. }
  160.  
  161. // Given the time and juggler, return a pointer to the specific hand
  162. HandPtr GetIndexedHand(JuggleHandle aJuggle, short time, short juggler, Boolean wrapIt)
  163. {
  164.     HandLoc    aLoc;
  165.     
  166.     aLoc.time = time;
  167.     aLoc.juggler = juggler;
  168.     
  169.     return GetHand(aJuggle, aLoc, wrapIt);
  170. }
  171.  
  172. // Add the throw to the connections data
  173. void AddThrow(JuggleHandle aJuggle, HandLoc from, HandLoc to)
  174. {
  175.     HandPtr    aHand;
  176.     
  177.     // set the sink of the source juggler
  178.     aHand = GetHand(aJuggle, from, false);
  179.     if(aHand != nil)
  180.         aHand->sink = to;
  181.  
  182.     // set the source of the sink juggler
  183.     aHand = GetHand(aJuggle, to, false);
  184.     if(aHand != nil)
  185.         aHand->source = from;
  186. }
  187.  
  188. // Convenience routine that takes the four individual coords
  189. void AddIndexedThrow(JuggleHandle aJuggle,
  190.                         short fromTime, short fromJuggler,
  191.                         short toTime, short toJuggler    )
  192. {
  193.     HandLoc    from, to;
  194.     
  195.     from.time = fromTime;
  196.     from.juggler = fromJuggler;
  197.     to.time = toTime;
  198.     to.juggler = toJuggler;
  199.     
  200.     AddThrow(aJuggle, from, to);
  201. }
  202.  
  203. // Remove the throw from the connections data
  204. void RemoveThrow(JuggleHandle aJuggle, HandLoc from, HandLoc to)
  205. {
  206.     HandPtr    aHand;
  207.     HandLoc    noLoc = {0, 0};
  208.     
  209.     // remove the sink of the source juggler
  210.     aHand = GetHand(aJuggle, from, false);
  211.     if(aHand != nil)
  212.         aHand->sink = noLoc;
  213.  
  214.     // remove the source of the sink juggler
  215.     aHand = GetHand(aJuggle, to, false);
  216.     if(aHand != nil)
  217.         aHand->source = noLoc;
  218. }
  219.  
  220. // Convenience routine that takes the four individual coords
  221. void RemoveIndexedThrow(JuggleHandle aJuggle,
  222.                         short fromTime, short fromJuggler,
  223.                         short toTime, short toJuggler    )
  224. {
  225.     HandLoc    from, to;
  226.     
  227.     from.time = fromTime;
  228.     from.juggler = fromJuggler;
  229.     to.time = toTime;
  230.     to.juggler = toJuggler;
  231.     
  232.     RemoveThrow(aJuggle, from, to);
  233. }
  234.     
  235. // Remove all the throws that touch this juggler from the connections data
  236. void ClearJugglerThrows(JuggleHandle aJuggle, short juggler)
  237. {
  238.     short    count;
  239.     HandPtr    hand;
  240.     
  241.     for(count = 0; count < (*aJuggle)->numCounts; count++)
  242.     {
  243.         hand = GetIndexedHand(aJuggle, count + 1, juggler, false);
  244.         if(hand != nil)
  245.         {
  246.             // if there's a source, remove it
  247.             if(hand->source.time != 0)
  248.                 RemoveIndexedThrow(aJuggle, hand->source.time, hand->source.juggler,
  249.                                             count + 1, juggler);
  250.             // if there's a sink, remove it
  251.             if(hand->sink.time != 0)
  252.                 RemoveIndexedThrow(aJuggle, count + 1, juggler,
  253.                                             hand->sink.time, hand->sink.juggler);
  254.         }
  255.     }
  256. }
  257.  
  258. // Copy throws data from one block of hands to another, preserving what we can. 
  259. // The new throws are assumed blank to begin with.
  260. void CopyThrows(HandHandle oldThrows, short oldJugglers, short oldCounts,
  261.                     HandHandle newThrows, short newJugglers, short newCounts)
  262. {
  263.     short    thisJuggler, thisTime, jugglerMax, countMax;
  264.     
  265.     // Step through the "intersection" of the two blocks, and only copy throws that are 
  266.     // entirely in that block
  267.     jugglerMax = min(oldJugglers, newJugglers);
  268.     countMax = min(oldCounts, newCounts);
  269.     for(thisJuggler = 1; thisJuggler <= jugglerMax; thisJuggler++)
  270.     {
  271.         for(thisTime = 1; thisTime <= countMax; thisTime++)
  272.         {
  273.             HandPtr    oldHand, newHand;
  274.             
  275.             oldHand = GetHandPtr(oldThrows, oldCounts, thisJuggler, thisTime);
  276.             newHand = GetHandPtr(newThrows, newCounts, thisJuggler, thisTime);
  277.             
  278.             // if there's a throw from here to somewhere in range, copy it
  279.             if(oldHand->sink.time > 0 &&
  280.                 oldHand->sink.time <= countMax && oldHand->sink.juggler <= jugglerMax)
  281.             {
  282.                 HandLoc    sink = oldHand->sink;
  283.                 
  284.                 // Copy the sink to the new hand
  285.                 newHand->sink = oldHand->sink;
  286.                 
  287.                 // Set the source of the sink, too.
  288.                 oldHand = GetHandPtr(oldThrows, oldCounts, sink.juggler, sink.time);
  289.                 newHand = GetHandPtr(newThrows, newCounts, sink.juggler, sink.time);
  290.                 newHand->source = oldHand->source;
  291.             }
  292.         }
  293.     }
  294. }
  295.                     
  296. // Find the nearest available receiver for a throw originating from the given coords
  297. // Available means no existing source
  298. HandLoc FindReceiver(JuggleHandle aJuggle, HandLoc from)
  299. {
  300.     short     time, juggler, maxTime;
  301.     HandLoc result = {0, 0};
  302.     HandPtr    hand;
  303.     
  304.     // --First try the self throw
  305.     
  306.     // Get our "next" hand
  307.     time = from.time + 1;    // next count
  308.     juggler = from.juggler; // "us"
  309.     hand = GetIndexedHand(aJuggle, time, juggler, true);
  310.     if(hand != nil)
  311.     {
  312.         if(hand->source.time == 0) // if not already receiving, this is it
  313.         {
  314.             result.time = time;
  315.             result.juggler = juggler;
  316.         }
  317.         else    // already receiving, try to "reply" to the same juggler
  318.         {
  319.             // now we know who to try to throw to, search forward through time
  320.             juggler = hand->source.juggler;
  321.             if((*aJuggle)->numCounts > 4) // limit to a quad for a reply
  322.                 maxTime = from.time + 4;
  323.             else
  324.                 maxTime = from.time + (*aJuggle)->numCounts; // one wrap, no more
  325.             for(time = from.time + 1; time <= maxTime; time++)
  326.             {
  327.                 hand = GetIndexedHand(aJuggle, time, juggler, true);
  328.                 if(hand != nil && hand->source.time == 0)
  329.                 {
  330.                     // This is it, save it
  331.                     result.time = time;
  332.                     result.juggler = juggler;
  333.                     break;
  334.                 }
  335.             }
  336.         }
  337.     }
  338.     
  339.     if(result.time == 0) // Nothing yet
  340.     {
  341.         // OK, do exhaustive search. Look forward in time, up to maxTime
  342.         maxTime = from.time + (*aJuggle)->numCounts; // one wrap, no more
  343.         for(time = from.time + 1; time <= maxTime; time++)
  344.         {
  345.             // and at every juggler, starting with 1
  346.             for(juggler = 1; juggler <= (*aJuggle)->numJugglers; juggler++)
  347.             {
  348.                 hand = GetIndexedHand(aJuggle, time, juggler, true);
  349.                 if(hand != 0 && hand->source.time == 0)
  350.                 {
  351.                     // This is it, save it
  352.                     result.time = time;
  353.                     result.juggler = juggler;
  354.                     break;
  355.                 }
  356.             }
  357.             if(result.time != 0)
  358.                 break; // bail if we're done
  359.         }
  360.     }
  361.     return result;
  362. }
  363.  
  364.  
  365.  
  366. void MoveJuggler(JuggleHandle aJuggle, short whichJuggler, short offset)
  367. {
  368.     // the juggler moved vertically: adjust jugglerMap and graphics
  369.     if(offset != 0)
  370.     {
  371.         short             startRow, newRow;
  372.         
  373.         // Figure out the row we're in
  374.         startRow = JugglerToRow(aJuggle, whichJuggler);
  375.         
  376.         // Figure out the row we're going to
  377.         newRow = startRow + offset;
  378.         
  379.         // Redo the jugglerMap
  380.         MoveJugglerInMap(aJuggle, whichJuggler, startRow, newRow);
  381.  
  382.         // Redo the juggler picture
  383.         MoveJugglerInPict(aJuggle, startRow, newRow);
  384.         
  385.         // caller should rebuild throws pict and force a redraw
  386.     }
  387. }
  388.  
  389. Boolean AddJuggler(JuggleHandle aJuggle)
  390. {
  391.     short        count, newRow; // same as new juggler number
  392.      gxShape        newJuggler = nil, aSep = nil;
  393.     gxLine        theLine;
  394.     
  395.     if(aJuggle == nil)
  396.         return false;
  397.         
  398.     // Get the new row
  399.     newRow = (*aJuggle)->numJugglers + 1;
  400.     
  401.     // Try to resize the throws.
  402.     if(ResizeThrows(aJuggle, newRow, (*aJuggle)->numCounts, true) == false)
  403.         return false;
  404.     
  405.     // Make the juggler and separator
  406.     // !!! This should probably be a separate routine (AddJugglerGraphics?)    
  407.     newJuggler = MakeJugglerShape(aJuggle, true, newRow);
  408.     aSep = MakeSeparatorShape(aJuggle, true);
  409.     if(newJuggler == nil || aSep == nil)
  410.     {
  411.         // Back out, resizing back down again, and disposing any shapes
  412.         ResizeThrows(aJuggle, newRow - 1, (*aJuggle)->numCounts, true);
  413.         if(newJuggler != nil)
  414.             GXDisposeShape(newJuggler);
  415.         if(aSep != nil)
  416.             GXDisposeShape(aSep);
  417.         return false;
  418.     }
  419.     
  420.     // All the allocation worked, now update the juggler graphics.
  421.     
  422.     // set juggler's position and add it to the picture
  423.     GXMoveShape(newJuggler, 0, (*aJuggle)->gridPt.y * (newRow - 1));
  424.     InsertPictureItem((*aJuggle)->jugglersPict, newRow, newJuggler, nil, nil, nil);
  425.  
  426.     // Same with separator
  427.     GXMoveShape(aSep, 0, (*aJuggle)->gridPt.y * (newRow - 2));
  428.     AddToPicture((*aJuggle)->sepsPict, aSep, nil, nil, nil);
  429.     
  430.     // Extend vertical separators
  431.     for(count = 1; count <= kNumVSeparators; count++)
  432.     {
  433.         GetPictureItem((*aJuggle)->sepsPict, count, &aSep, nil, nil, nil);
  434.         GXGetLine(aSep, &theLine);
  435.         theLine.last.y += (*aJuggle)->gridPt.y;
  436.         GXSetLine(aSep, &theLine);
  437.     }
  438.     
  439.     // Add the new juggler to the screen map
  440.     (*aJuggle)->jugglerMap[newRow - 1] = newRow;
  441.     
  442.     // Make sure any changes in the throws are shown.
  443.     RebuildThrowsPict(aJuggle);
  444.     
  445.     // Set the content size appropriately
  446.     ResetJuggleContentSize(aJuggle);
  447.  
  448.     // Mark dirty
  449.     (*aJuggle)->dirty = true;
  450.     
  451.     return true;
  452. }
  453.  
  454. // Just creates a new window and copies throws data over, returning the new window
  455. WindowPtr ResizeJuggle(WindowPtr wind)
  456. {
  457.     WindowPtr         newWind;
  458.     JuggleHandle    oldJuggle, newJuggle;
  459.     Str255            title;
  460.     
  461.     if(wind == nil) // safety check
  462.         return nil;
  463.     
  464.     // Set up a new juggle
  465.     newWind = AppNew(0, 0); // Ask user how big to make it
  466.     if(newWind == nil)
  467.         return nil;
  468.     
  469.     // Copy over window name
  470.     GetWTitle(wind, title);
  471.     SetWTitle(newWind, title);
  472.  
  473.     // Get the two juggle structures
  474.     oldJuggle = GetWindowJuggle(wind);
  475.     newJuggle = GetWindowJuggle(newWind);
  476.     
  477.     // Copy throws data from the old one to the new one
  478.     CopyThrows((*oldJuggle)->theThrows, (*oldJuggle)->numJugglers, (*oldJuggle)->numCounts,
  479.                 (*newJuggle)->theThrows, (*newJuggle)->numJugglers, (*newJuggle)->numCounts);
  480.     
  481.     // Make sure any changes in the throws are shown.
  482.     RebuildThrowsPict(newJuggle);
  483.     
  484.     // Copy over the fileSpec
  485.     (*newJuggle)->fileSpec = (*oldJuggle)->fileSpec;
  486.     
  487.     // Throw away old one
  488.     AppClose(wind);
  489.                 
  490.     // Mark new one dirty
  491.     (*newJuggle)->dirty = true;
  492.     
  493.     return newWind;
  494. }